package cn.huijielong.modules.oss.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.Gson;
import cn.huijielong.common.constants.OssFileType;
import cn.huijielong.common.exception.VSException;
import cn.huijielong.common.utils.Query;
import cn.huijielong.common.utils.RD;
import cn.huijielong.common.utils.ImgCompress;
import cn.huijielong.common.utils.Status;
import cn.huijielong.common.utils.VSUtil;
import cn.huijielong.common.validator.Assert;
import cn.huijielong.modules.oss.cloud.CloudStorage;
import cn.huijielong.modules.oss.cloud.OSSConfig;
import cn.huijielong.modules.oss.cloud.OSSFactory;
import cn.huijielong.modules.oss.dao.OssFileDao;
import cn.huijielong.modules.oss.entity.BucketType;
import cn.huijielong.modules.oss.entity.Oss;
import cn.huijielong.modules.oss.entity.OssBucket;
import cn.huijielong.modules.oss.entity.OssFileEntity;
import cn.huijielong.modules.oss.entity.OssProps;
import cn.huijielong.modules.oss.service.OssFileService;

import io.jsonwebtoken.lang.Collections;


@Service("ossFileService")
public class OssFileServiceImpl implements OssFileService {
	@Autowired
    private OssProps sssProps;
	
	private Logger logger = LoggerFactory.getLogger(OssFileServiceImpl.class);
	private List<String> images=Arrays.asList("bmp","jpg","jpeg","png","gif");
	private List<String> archives=Arrays.asList("doc","docx","txt","pdf","xls","xlsx","zip","rar");
	@Autowired
	private OssFileDao ossFileDao;
	
	@Override
	public OssFileEntity queryObject(Long id){
		return ossFileDao.queryObject(id);
	}
	
	@Override
	public List<OssFileEntity> queryList(Long tableId,String tableType){
		Assert.isBlank(tableType, "来源类别(tableType)不能为空");
		Assert.isNull(tableId, "来源类别(tableId)不能为空");
		Query q=Query.empty().addParam("tableType", tableType).addParam("tableId", tableId);
		List<OssFileEntity> ossFiles = ossFileDao.queryList(q);
		String ossname=sssProps.getName();
		Map<String,CloudStorage> storages=new HashMap<>();
		//私有文件url转换
		for(OssFileEntity ofile:ossFiles) {
			if(OssFileEntity.STORE_TYPE_1.equals(ofile.getStoreType())) {
				String bucketName=ofile.getBucket();
				CloudStorage cloudStorage=storages.get(ossname+bucketName);
				if(cloudStorage==null) {
					cloudStorage=OSSFactory.build(ossname, bucketName);
					storages.put(ossname+bucketName, cloudStorage);
				}
				String url=cloudStorage.getPrivateUrl(ofile.getUrl());
				ofile.setUrl(url);
			}
		}
		for(Map.Entry<String, CloudStorage> entry:storages.entrySet()) {
        	entry.getValue().close();
        }
		return ossFiles;
	}
	
	@Override
	public int queryTotal(Map<String, Object> map){
		return ossFileDao.queryTotal(map);
	}
	
	@Override
	public RD save(OssFileEntity ossFile){
		logger.info("OSS管理->文件保存，处理开始");
		Assert.isBlank(ossFile.getTableType(), "来源类别(tableType)不能为空");
		Assert.isBlank(ossFile.getStoreType(), "存储类别(storeType)不能为空");
		Assert.isNull(ossFile.getTableId(), "来源类别(tableId)不能为空");
		String ossname=sssProps.getName();
		Assert.isNull(ossname, "配置文件里没有配置oss.name的值");
		
		int orderNum=ossFile.getOrderNum()==null?1:ossFile.getOrderNum().intValue();
		List<MultipartFile> files=ossFile.getUploadFile();
		//存文件信息
		if(files==null) {
			logger.warn("本次没有可上传的文件");
			return RD.error("本次没有可上传的文件");
		}
		
		ArrayNode arrayNode=VSUtil.getMapper().createArrayNode();
		Map<String,CloudStorage> storages=new HashMap<>();
		
        for(int i=0;i<files.size();i++){
        	MultipartFile file=files.get(i);
    		String name = java.util.UUID.randomUUID().toString().replaceAll("-", "");
    		String filename = file.getOriginalFilename();
    		ObjectNode node=VSUtil.getMapper().createObjectNode();
			node.put("fileName", filename);
        	//存入七牛云
        	try {
        		String suffix = filename.substring(filename.lastIndexOf(".")+1);
				String suffixLower=suffix.toLowerCase();
				String fileKey=null;
				int bucketType=0;
				String fileType=null;//0图片1文档
				
				//上传的是图片文件
				if(images.contains(suffixLower)) {
					if(ossFile.getStoreType().equals(OssFileEntity.STORE_TYPE_1)) {
						bucketType=sssProps.getImagesPrivate();
					}else {
						bucketType=sssProps.getImagesOpen();
					}
					
					fileType=OssFileType.IMAGE;
				}else if(archives.contains(suffixLower)){//上传的是文档文件
					if(ossFile.getStoreType().equals(OssFileEntity.STORE_TYPE_1)) {
						bucketType=sssProps.getArchivesPrivate();
					}else {
						bucketType=sssProps.getArchivesOpen();
					}
					fileType=OssFileType.ARCHIVE;
				}else {//上传的是不支持的文件格式
					throw new VSException(" 不支持该类型文件的存储！");
				}
				String bucketName=BucketType.convert(ossname, bucketType);
				fileKey=new StringBuilder(ossFile.getTableType()).append("/").append(ossFile.getTableId()).append("/").append(name).append(".").append(suffix).toString();
				CloudStorage cloudStorage=storages.get(ossname+bucketName);
				if(cloudStorage==null) {
					cloudStorage=OSSFactory.build(ossname, bucketName);
					storages.put(ossname+bucketName, cloudStorage);
				}
				
				byte[] fileData=file.getBytes();
				int fileSize=fileData.length/(1024*1024);//M
				//图片大于1M，压缩
				if(fileSize>1) {
					fileData=ImgCompress.run(fileData,2000);
				}
				fileSize=fileData.length/1024;//K
				
				String url=cloudStorage.upload(file.getBytes(), fileKey);
				if(i==0) {
					ossFile.setFirstUrl(url);
				}
				ossFile.setUrl(url);
				ossFile.setBucket(bucketName);
				ossFile.setFileType(fileType);
				int filenameLen=filename.getBytes().length;
				if(filenameLen>=60) {
					filename=filename.substring(filenameLen-60);
				}
				ossFile.setName(filename);
				ossFile.setFileSize(fileSize);
				ossFile.setOrderNum(orderNum++);
				ossFileDao.save(ossFile);
				node.put("url", url);
				node.put("fileType", fileType);
				node.put("imageId", ossFile.getId());
				node.put("status", Status.SUCCESS);
				ossFile.setId(null);
			} catch (Exception e) {
				logger.error("文件{}上传出错",filename,e);
				node.put("status", Status.FAIL);
			}
        	arrayNode.add(node);
        }
        for(Map.Entry<String, CloudStorage> entry:storages.entrySet()) {
        	entry.getValue().close();
        }
        logger.info("OSS管理->文件保存，处理结束");
        return RD.ok().put("datas", arrayNode);
	}
	
	@Override
	public RD update(OssFileEntity ossFile){
		logger.info("OSS管理->文件修改开始");
		Assert.isBlank(ossFile.getTableType(), "来源类别(tableType)不能为空");
		Assert.isBlank(ossFile.getStoreType(), "存储类别(storeType)不能为空");
		Assert.isNull(ossFile.getTableId(), "来源类别(tableId)不能为空");
		String ossname=sssProps.getName();
		Assert.isNull(ossname, "配置文件里没有配置oss.name的值");
		Query q=Query.empty().addParam("tableType", ossFile.getTableType()).addParam("tableId", ossFile.getTableId());
		List<OssFileEntity> ossFiles=ossFileDao.queryList(q);
		String[] persistUrls=ossFile.getPersistUrls();
		//删除云文件时，保留指定的文件
		for(int i=0;persistUrls!=null && i<persistUrls.length;i++) {
			for(int n=0;n<ossFiles.size();n++) {
				if(ossFiles.get(n).getUrl().equals(persistUrls[i])) {
					ossFiles.remove(n);
					break;
				}
			}
		}
		try {
			logger.info("OSS管理->文件修改，删除云文件");
			deleteOssFiles(ossname, ossFiles);
			
			//删除数据库文件
			logger.info("OSS管理->文件修改，删除数据库中对应文件记录");
			ossFileDao.deleteFilesByOssFileEntity(ossFile);
			RD r=null;
			List<MultipartFile> files=ossFile.getUploadFile();
			if(!Collections.isEmpty(files)){
				//保存数据
				logger.info("OSS管理->文件修改，保存本次新增的文件");
				r= save(ossFile);
			}else {
				r=RD.ok("云文件修改成功");
			}
			logger.info("OSS管理->文件修改，处理成功");
			return r;
		}catch(Exception e) {
			logger.error("OSS管理->文件修改失败",e);
			throw e;
		}
		
	}

	private void deleteOssFiles(String ossname, List<OssFileEntity> ossFiles) {
		Map<String,List<String>> delBucket=new HashMap<>();
		for(OssFileEntity of:ossFiles) {
			List<String> keys=delBucket.get(of.getBucket());
			if(keys==null) {
				keys=new ArrayList<String>();
			}
			keys.add(of.getUrl());
			delBucket.put(of.getBucket(), keys);
		}
		//删除云文件
		delBucket.entrySet().forEach(entry->{
			CloudStorage cloudStorage=OSSFactory.build(ossname, entry.getKey());
			cloudStorage.deleteBatch(entry.getValue().toArray(new String[] {}));
			cloudStorage.close();
		});
	}
	
	@Override
	public void delete(Long tableId,String tableType){
		Assert.isBlank(tableType, "来源类别(tableType)不能为空");
		Assert.isNull(tableId, "来源类别(tableId)不能为空");
		String ossname=sssProps.getName();
		Assert.isNull(ossname, "配置文件里没有配置oss.name的值");
		Query q=Query.empty().addParam("tableType", tableType).addParam("tableId", tableId);
		List<OssFileEntity> files=ossFileDao.queryList(q);
		deleteOssFiles(ossname,files);
		ossFileDao.deleteFiles(tableId,tableType);
	}
	
	
	@Override
	public OSSConfig getOSSConfig() {
		//从redis里取，没有值再从mysql取
		
		OSSConfig config=new OSSConfig();
		List<Oss> osses=ossFileDao.getOsses();
		List<OssBucket> buckets=ossFileDao.getOssBuckets();
		for(Oss oss:osses) {
			Oss oss2=new Gson().fromJson(oss.getConfig(), Oss.class);
			oss2.setId(oss.getId());
			oss2.setName(oss.getName());
			for(OssBucket b:buckets) {
				if(oss.getId()==b.getOssId()) {
					oss2.addBucket(b);
				}
			}
			config.add(oss2);
		}
		return config;
	}

	@Override
	public List<String> queryFiles(Long tableId, String tableType) {
		Assert.isBlank(tableType, "来源类别(tableType)不能为空");
		Assert.isNull(tableId, "来源类别(tableId)不能为空");
		Query q=Query.empty().addParam("tableType", tableType).addParam("tableId", tableId);
		return ossFileDao.queryFiles(q);
	}

	@Override
	public List<String> queryFiles(Long tableId, String tableType, String tag) {
		Assert.isBlank(tableType, "来源类别(tableType)不能为空");
		Assert.isNull(tableId, "来源类别(tableId)不能为空");
		Assert.isNull(tableId, "文件标签(tag)不能为空");
		Query q=Query.empty().addParam("tableType", tableType).addParam("tableId", tableId).addParam("tag", tag);
		return ossFileDao.queryFiles(q);
	}

	@Override
	public String getPrivateUrl(String fileKey) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Map<String, String> getPrivateUrl(List<String> fileKeys) {
		// TODO Auto-generated method stub
		return null;
	}
	
}
